package com.sghj.config.redis;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.UUID;

/**
 * @author ZhongKai
 * @date 2022-06-24 15:35
 */

@Aspect
@Component
public class QueuingPollAspect {

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Around (value = "@annotation(com.sghj.config.redis.QueuingPoll)")
    public Object translateReturning(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String tenantId = "null";
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            tenantId = request.getParameter("tenantId");
        }
        // Thread currentThread = Thread.currentThread();
        // 线程 ID 是唯⼀的，并且在其⽣命周期内保持不变。当⼀个线程终⽌时，这个线程 ID 可能会被重⽤。
        // String threadId = String.valueOf(currentThread.getId());
        // 在多实例多情况下线程ID可能会导致重复，所以使⽤UUID
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        // 相同的租户放⼊同⼀个redis队列⾥，实现同租户串⾏不同的租户并⾏
        String key = "jenson:list-thread:" + tenantId;
        redisTemplate.opsForList().rightPush(key, uuid);
        boolean waitFlag = Boolean.TRUE;
        while (waitFlag) {
            waitFlag = Boolean.FALSE;
//            System.out.println(threadName + "轮询查看是否轮到⾃⼰");
            List<String> top = redisTemplate.opsForList().range(key, 0, 0);
            if (top != null && top.size() > 0) {
                // redis ⾥有数据
                if (!uuid.equals(top.get(0))) {
                    // 队列顶部不是该接⼝，线程等待
                    waitFlag = Boolean.TRUE;
                }
            }
            if (waitFlag) {
                // 根据接⼝执⾏平均时长来适度调整休眠时间，休眠时会让出cpu给其他的线程
                Thread.sleep(10);
            }
        }
        Object result = proceedingJoinPoint.proceed();
        // 执⾏结束，推出队列顶端元素
        redisTemplate.opsForList().leftPop(key);
        return result;
    }
    @AfterThrowing(value = "@annotation(com.sghj.config.redis.QueuingPoll)", throwing = "e")
    public void throwingAdvice(JoinPoint joinPoint, Exception e) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String tenantId = "null";
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            tenantId = request.getParameter("tenantId");
        }
        String key = "jenson:list-thread:" + tenantId;
        // 抛出错误时也要推出队列顶端元素，否则后⾯的接⼝就堵死了
        redisTemplate.opsForList().leftPop(key);
    }
}
